﻿/*
 * Copyright (c) 2025 粤港澳大湾区（广东）国创中心
 *
 * This file is a part of NctiCAEPre.
 *
 * NctiCAEPre is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 *
 */

#include "IPCManager.h"
#include <QApplication>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>

namespace NCTI {

    static std::string toString(const QString& qstr) 
    {
        QByteArray ba = qstr.toUtf8();
        return ba.toStdString();
    }

    IPCManager::IPCManager(QObject* parent) : QObject(parent)
    {
        startLocalServer();
    }

    IPCManager::~IPCManager()
    {
        stopLocalServer();
    }

    bool IPCManager::startLocalServer()
    {
        if (mServer) {
            qWarning() << "Local server already exists";
            return false;
        }

        mServer = std::make_unique<QLocalServer>(this);

        // 移除可能存在的旧连接
        QString serverName = QStringLiteral("ncti_qt_launcher_%1").arg(QApplication::applicationPid());
        QLocalServer::removeServer(serverName);

        // 监听连接
        if (!mServer->listen(serverName)) {
            qWarning() << "Failed to start local server:" << mServer->errorString();
            mServer.reset();
            return false;
        }

        connect(mServer.get(), &QLocalServer::newConnection, this, &IPCManager::handleNewConnection);

        qDebug() << "Local server started:" << serverName;
        return true;
    }

    void IPCManager::stopLocalServer()
    {
        if (mServer) {
            mServer->close();
            mServer.reset();
        }
        mServerName.clear();
    }

    void IPCManager::handleNewConnection()
    {
        QLocalSocket* socket = mServer->nextPendingConnection();
        if (!socket) {
            qWarning() << "Failed to get next pending connection";
            return;
        }

        connect(socket, &QLocalSocket::readyRead, this, &IPCManager::readSocketData);

        connect(socket, &QLocalSocket::disconnected, [this, socket]() {
            qDebug() << "Socket disconnected";
            Q_EMIT sigSocketDisconnected(socket);
            removeEmbeddedWindow(socket);
            socket->deleteLater();
            });

        // 自动清理无效连接
#if QT_VERSION_MAJOR >= 6
        connect(socket, &QLocalSocket::errorOccurred, this,
            [socket](QLocalSocket::LocalSocketError error) {
                qDebug() << "Socket error:" << error;
                socket->deleteLater();
            });
#else
        connect(socket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error), this,
            [socket](QLocalSocket::LocalSocketError error) {
                qDebug() << "Socket error:" << error;
                socket->deleteLater();
            });
#endif
    }

    void IPCManager::readSocketData()
    {
        QLocalSocket* socket = qobject_cast<QLocalSocket*>(sender());
        if (!socket) { return; }

        QByteArray data = socket->readAll();
        QString message(data);

        try {
            json j = json::parse(message.toStdString());
            MessageData fromData = j;

            if (fromData.type == MessageType::MT_INVALID) {
                qWarning() << "Invalid message type received";
                return;
            }

            // 根据消息类型分发到不同的信号
            if (fromData.type == MessageType::MT_WINDOW_ID) {
                Q_EMIT sigWindowIdMessage(socket, fromData);
            }
            else if (fromData.type == MessageType::MT_EXIT_SAVE) {
                Q_EMIT sigExitSaveMessage(fromData);
            }
            else if (fromData.type == MessageType::MT_DOC_DIRTY) {
                Q_EMIT sigDocDirtyMessage(fromData);
            }
            else if (fromData.type == MessageType::MT_UPDATE) {
                Q_EMIT sigDataUpdateMessage(fromData);
            }
        }
        catch (const json::exception& e) {
            qWarning() << "IPCManager::readSocketData(), JSON parse error:" << e.what();
        }
    }

    bool IPCManager::sendDocDirtyMessage(int64_t windowId, const QString& text)
    {
        MessageData sendData;
        sendData.type = MessageType::MT_DOC_DIRTY;
        sendData.window_id = windowId;
        sendData.file_name = toString(text);
        return sendMessageData(sendData);
    }

    void IPCManager::sendExitMessageToAllClient()
    {
        for (const auto& pair : mEmbeddedWindowData) {
            const auto& embedded = pair.second;
            if (embedded.windowId > 0 && embedded.windowId != MAIN_PAGE_ID) {
                MessageData sendData;
                sendData.type = MessageType::MT_EXIT;
                sendData.window_id = embedded.windowId;
                sendMessageData(sendData);
            }
        }
    }

    void IPCManager::sendExitMessage(int64_t windowId)
    {
        MessageData sendData;
        sendData.type = MessageType::MT_EXIT;
        sendData.window_id = windowId;
        sendMessageData(sendData);
    }

    void IPCManager::sendExitSaveMessage(int64_t windowId, const QString& savePath)
    {
        if (savePath.isEmpty()) { return; }

        MessageData sendData;
        sendData.type = MessageType::MT_EXIT_SAVE;
        sendData.window_id = windowId;
        sendData.file_path = toString(savePath);
        sendMessageData(sendData);
    }

    void IPCManager::sendDocRefreshMessage(int64_t windowId)
    {
        MessageData sendData;
        sendData.type = MessageType::MT_DOC_REFRESH;
        sendData.window_id = windowId;
        sendMessageData(sendData);
    }

    bool IPCManager::sendMessageData(const MessageData& d)
    {
        auto it = mEmbeddedWindowData.find(d.window_id);
        if (it == mEmbeddedWindowData.end() || !it->second.socket || !it->second.socket->isOpen()) {
            qWarning() << "socket is error";
            return false;
        }

        QLocalSocket* socket = it->second.socket;

        try {
            json j = d;
            socket->write(QString::fromStdString(j.dump()).toUtf8());
            socket->flush();
        }
        catch (const json::exception& e) {
            qWarning() << "IPCManager::sendMessageData(), JSON parse error:" << e.what();
            return false;
        }

        return true;
    }

    void IPCManager::addEmbeddedWindow(int64_t windowId, QWidget* widget, QLocalSocket* socket)
    {
        EmbeddedWndData data;
        data.widget = widget;
        data.socket = socket;
        data.windowId = windowId;
        mEmbeddedWindowData[windowId] = data;
    }

    EmbeddedWndData IPCManager::removeEmbeddedWindow(int windowId)
    {
        EmbeddedWndData data;
        auto it = mEmbeddedWindowData.find(windowId);
        if (it != mEmbeddedWindowData.end()) {
            data = it->second;
            mEmbeddedWindowData.erase(it);
        }
        return data;
    }

    EmbeddedWndData IPCManager::removeEmbeddedWindow(QLocalSocket* socket)
    {
        EmbeddedWndData data;
        for (auto it = mEmbeddedWindowData.begin(); it != mEmbeddedWindowData.end(); ++it) {
            if (it->second.socket == socket) {
                data = it->second;
                mEmbeddedWindowData.erase(it);
                break;
            }
        }
        return data;
    }

    EmbeddedWndData IPCManager::getEmbeddedWindow(int windowId)
    {
        EmbeddedWndData data;
        auto it = mEmbeddedWindowData.find(windowId);
        if (it != mEmbeddedWindowData.end()) {
            data = it->second;
        }
        return data;
    }

    int IPCManager::getEmbeddedWindowCount() const
    {
        return mEmbeddedWindowData.size();
    }

    void IPCManager::refreshDocView(QWidget* widget)
    {
        for (const auto& pair : mEmbeddedWindowData) {
            const auto& embedded = pair.second;
            if (embedded.windowId > 0 && widget == embedded.widget) {
                sendDocRefreshMessage(embedded.windowId);
                break;
            }
        }
    }
    
    QString IPCManager::getFullServerName()
    {
        if (mServer) {
            return mServer->fullServerName();
        }
        return QString();
    }
}